#include <kew/c-vm.h>
#include <kew/stdlib.h>

#define block_method(BLOCK) (((KewBlockObject *) (BLOCK))->method)

static void
ensured_action_callback(KewDynamicEnvironment *env, KewActionObject *action)
{
    kewSend(env, receiver, sel_call, NULL_CALL_SHAPE, NULL, NULL);
}

KEW_EXPORT_METHOD(kew_action_call_ensuring)
{
    KewActionObject *action = kewCheckAction(args[0]);
    KEW_ENSURED_FRAME_BEGIN(env, ensuredActionCallback, action);
    
    /* FIXME: correct this so it passes on the rest of the arguments and also returns results */
    kewSend(env, receiver, sel_call, NULL_CALL_SHAPE, NULL, NULL);
    
    KEW_ENSURED_FRAME_END(env);
}

KEW_EXPORT_METHOD(kew_action_forever)
{
	while (1) {
		KewObject **temp;
		block_method(receiver) (receiver, args, num_args, results, num_results);
		temp = args;
		args = results;
		results = temp;
	}
}

KEW_EXPORT_METHOD(kew_action_for)
{
	KewObject *reader;
	KewObject **accumulator;
	KewObject *send_results[1];
	unsigned accumulator_size = num_results;

	accumulator = g_new(KewObject *, accumulator_size + 1);
	memcpy(accumulator, args + 1, accumulator_size * sizeof(KewObject *));
	KewSend(args[0], "reader", NULL, 0, send_results, 1);
	reader = send_results[0];

	while (KewSend(reader, "empty", NULL, 0, send_results, 1), KewIsFalse(send_results[0])) {
		KewSend(reader, "read", NULL, 0, send_results, 1);
		accumulator[accumulator_size] = send_results[0];
		block_method(receiver) (receiver, accumulator, accumulator_size + 1, accumulator, accumulator_size);
	}

	memcpy(results, accumulator, accumulator_size * sizeof(KewObject *));
	g_free(accumulator);
}

KEW_EXPORT_METHOD(kew_action_while)
{
	KewObject **send_args;
	KewObject **send_results;
	unsigned accumulator_size = num_args;

	send_args = g_new(KewObject *, accumulator_size + 1);
	send_results = g_new(KewObject *, accumulator_size + 1);

	memcpy(send_args, args, accumulator_size * sizeof(KewObject *));

	do {
		KewObject **temp;

		block_method(receiver) (receiver, send_args, accumulator_size, send_results, accumulator_size + 1);

		temp = send_args;
		send_args = send_results;
		send_results = temp;
	} while (KewIsTrue(send_args[accumulator_size]));

	memcpy(results, send_args, accumulator_size * sizeof(KewObject *));
	g_free(send_args);
	g_free(send_results);
}


KEW_METHOD(kew_action_do)
{
	block_method(receiver) (receiver, args, num_args, results, num_results);
}

KewObject *
kew_block_new(KewMethod method, unsigned argument_cardinality, unsigned result_cardinality, unsigned context_size)
{
	KewBlockObject *block;

	block = (KewBlockObject *) KewNewObjectWithContext(&KewBlockType, context_size);
	block->method = method;
	block->argument_cardinality = argument_cardinality;
	block->result_cardinality = result_cardinality;
	return (KewObject *) block;
}
